home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / TSR / TSRSRC35 / MAPMEM.PAS < prev    next >
Pascal/Delphi Source File  |  1993-10-21  |  31KB  |  1,034 lines

  1. {**************************************************************************
  2. *   MAPMEM - Reports system memory blocks.                                *
  3. *   Copyright (c) 1986,1993 Kim Kokkonen, TurboPower Software.            *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************
  6. *   version 1.0 1/2/86                                                    *
  7. *   :                                                                     *
  8. *   long intervening history                                              *
  9. *   :                                                                     *
  10. *   version 3.0 9/24/91                                                   *
  11. *     completely rewritten for DOS 5 compatibility                        *
  12. *     add upper memory reporting                                          *
  13. *     add XMS reporting                                                   *
  14. *     add free memory report                                              *
  15. *     report on EMS handle names                                          *
  16. *     change command line switches                                        *
  17. *     add check for TSR feature                                           *
  18. *     add Quiet option (useful with "check for" option only)              *
  19. *     add summary report                                                  *
  20. *   version 3.1 11/4/91                                                   *
  21. *     fix bug in EMS handle reporting                                     *
  22. *     fix problem in getting name of TSR that shrinks environment (FSP)   *
  23. *     prevent from keeping interrupt 0                                    *
  24. *     fix source naming of WriteChained vs WriteHooked                    *
  25. *     show command line and vectors even if lower part of PSP is          *
  26. *       overwritten (DATAPATH)                                            *
  27. *     wouldn't find (using /C) a program whose name was stored in         *
  28. *       lowercase in the environment (Windows 3.0)                        *
  29. *   version 3.2 11/22/91                                                  *
  30. *     generalize high memory support                                      *
  31. *     handle some DRDOS 6.0 conventions                                   *
  32. *     fix indentation problem in raw extended memory report               *
  33. *   version 3.3 1/8/92                                                    *
  34. *     /C getname wasn't finding TSRs in high memory                       *
  35. *     increase stack space                                                *
  36. *     new features for parsing and getting command line options           *
  37. *   version 3.4 2/14/92                                                   *
  38. *     fix bug in memory reported for device memory blocks                 *
  39. *     add /L option to turn off low memory reporting                      *
  40. *     change /C to find TSRS only in low memory unless /U specified       *
  41. *     add a new test to validate command line strings of mcbs             *
  42. *   version 3.5 10/18/93                                                  *
  43. *     no change                                                           *
  44. ***************************************************************************
  45. *   telephone: 719-260-6641, CompuServe: 76004,2611.                      *
  46. *   requires Turbo Pascal 6 or 7 to compile.                              *
  47. ***************************************************************************}
  48.  
  49. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  50. {$M 4096,2048,655360}
  51. {.$DEFINE MeasureStack}  {Activate to measure stack usage}
  52.  
  53. program MapMem;
  54.  
  55. uses
  56.   Dos,
  57.   MemU,
  58.   Xms,
  59.   Ems;
  60.  
  61. const
  62.   CheckTSR : Boolean = False;          {'C'}
  63.   ShowEmsMem : Boolean = False;        {'E'}
  64.   ShowFree : Boolean = False;          {'F'}
  65.   UseWatch : Boolean = True;           {'H'}
  66.   UseLoMem : Boolean = True;           {'L'}
  67.   Quiet : Boolean = False;             {'Q'}
  68.   ShowSummary : Boolean = False;       {'S'}
  69.   UseHiMem : Boolean = False;          {'U'}
  70.   Verbose : Boolean = False;           {'V'}
  71.   ShowExtMem : Boolean = False;        {'X'}
  72.  
  73. var
  74.   TotalMem : LongInt;
  75.   TopSeg : Word;
  76.   HiMemSeg : Word;
  77.   WatchPsp : Word;
  78.   ShowDevices : Boolean;
  79.   ShowSegments : Boolean;
  80.   ShowBlocks : Boolean;
  81.   ShowFiles : Boolean;
  82.   ShowVectors : Boolean;
  83.   GotXms : Boolean;
  84.   SizeLen : Byte;
  85.   NameLen : Byte;
  86.   CmdLen : Byte;
  87.   UmbLinkStatus : Boolean;
  88.   SaveExit : Pointer;
  89.   TsrName : string[79];
  90.   {$IFDEF MeasureStack}
  91.   I : Word;
  92.   {$ENDIF}
  93.  
  94. const
  95.   FreeName  : string[10] = '---free---';
  96.   TotalName : string[10] = '---total--';
  97.  
  98. const
  99.   VerboseIndent = 5;
  100.   NoShowVecSeg = $FFFE;
  101.   ShowVecSeg   = $FFFF;
  102.  
  103.   procedure SafeExit; far;
  104.   begin
  105.     ExitProc := SaveExit;
  106.     SwapVectors;
  107.   end;
  108.  
  109.   function GetName(M : McbPtr; var Devices : Boolean) : String;
  110.     {-Return a name for Mcb M}
  111.   const
  112.     EnvName : array[boolean] of string[4] = ('', 'env');
  113.     DatName : array[boolean] of string[4] = ('', 'data');
  114.   var
  115.     PspSeg : Word;
  116.     IsCmd : Boolean;
  117.   begin
  118.     Devices := False;
  119.     PspSeg := M^.Psp;
  120.  
  121.     if (PspSeg = 0) or (PspSeg = PrefixSeg) then
  122.       GetName := FreeName
  123.     else if PspSeg = 8 then begin
  124.       GetName := 'sys data';
  125.       if DosV = 5 then
  126.         if (M^.Name[1] = 'S') and (M^.Name[2] = 'D') then begin
  127.           GetName := 'cfg info';
  128.           Devices := True;
  129.         end;
  130.     end else if (PspSeg < 8) or (PspSeg >= $FFF0) then
  131.       GetName := 'unknown'
  132.     else if PspSeg = OS(M).S+1 then begin
  133.       {program block}
  134.       IsCmd := (PspSeg = MemW[PspSeg:$16]);
  135.       if (not IsCmd) and (DosV > 2) and HasEnvironment(HiMemSeg, M) then
  136.         GetName := NameFromEnv(M)
  137.       else if DosV >= 4 then
  138.         GetName := NameFromMcb(M)
  139.       else if IsCmd then
  140.         GetName := 'command'
  141.       else if DosVT >= $031E then
  142.         GetName := NameFromMcb(M)
  143.       else
  144.         GetName := 'n/a';
  145.     end else if MemW[PspSeg:$2C] = OS(M).S+1 then
  146.       GetName := EnvName[Verbose]+'('+GetName(Ptr(PspSeg-1, 0), Devices)+')'
  147.     else
  148.       GetName := DatName[Verbose]+'('+GetName(Ptr(PspSeg-1, 0), Devices)+')';
  149.   end;
  150.  
  151.   function ValidPsp(PspSeg : Word) : Boolean;
  152.     {-Return True if PspSeg is a valid Psp}
  153.   begin
  154.     if ((PspSeg >= 0) and (PspSeg <= 8)) or
  155.        (PspSeg = PrefixSeg) or
  156.        (PspSeg >= $FFF0) then
  157.       ValidPsp := False
  158.     else
  159.       ValidPsp := True;
  160.   end;
  161.  
  162.   function GetFiles(M : McbPtr) : Word;
  163.     {-Return number of open files for given Mcb's Psp}
  164.   type
  165.     HandleTable = array[0..65520] of Byte;
  166.   var
  167.     PspSeg : Word;
  168.     O : Word;
  169.     Files : Word;
  170.     FileMax : Word;
  171.     TablePtr : ^HandleTable;
  172.   begin
  173.     PspSeg := M^.Psp;
  174.     if (PspSeg <> OS(M).S+1) or not ValidPsp(PspSeg) or
  175.        (MemW[PspSeg:$50] <> $21CD) then begin
  176.       GetFiles := 0;
  177.       Exit;
  178.     end;
  179.     {Deal with expanded handle tables in DOS 3.0 and later}
  180.     if DosV >= 3 then begin
  181.       FileMax := MemW[M^.Psp:$32];
  182.       TablePtr := Pointer(MemL[M^.Psp:$34]);
  183.     end else begin
  184.       FileMax := 20;
  185.       TablePtr := Ptr(M^.Psp, $18);
  186.     end;
  187.  
  188.     Files := 0;
  189.     for O := 0 to FileMax-1 do
  190.       case TablePtr^[O] of
  191.         0, 1, 2, $FF : {standard handle or not open} ;
  192.       else
  193.         Inc(Files);
  194.       end;
  195.     GetFiles := Files;
  196.   end;
  197.  
  198.   function GetCmdLine(M : McbPtr) : String;
  199.     {-Return command line for program}
  200.   var
  201.     PspSeg : Word;
  202.     Len : Byte;
  203.     S : String[127];
  204.   begin
  205.     PspSeg := M^.Psp;
  206.     if (PspSeg <> OS(M).S+1) or not ValidPsp(PspSeg) then begin
  207.       GetCmdLine := '';
  208.       Exit;
  209.     end;
  210.     Move(Mem[PspSeg:$80], S, 127);
  211.     if S <> '' then begin
  212.       Len := Length(S);
  213.       if (Len > 127) or (S[Len+1] <> ^M) then
  214.         S := ''
  215.       else
  216.         StripNonAscii(S);
  217.       if S = '' then
  218.         S := 'n/a';
  219.     end;
  220.     while (Length(S) > 0) and (S[1] = ' ') do
  221.       Delete(S, 1, 1);
  222.     GetCmdLine := S;
  223.   end;
  224.  
  225.   procedure WriteHooked(LowSeg, HighSeg : Word; StartCol, WrapCol : Byte);
  226.     {-Write vectors that point into specified region of memory}
  227.   var
  228.     Vectors : array[0..255] of Pointer absolute 0:0;
  229.     Vec : Pointer;
  230.     LoL : LongInt;
  231.     HiL : LongInt;
  232.     VeL : LongInt;
  233.     V : Byte;
  234.     Col : Byte;
  235.   begin
  236.     LoL := LongInt(LowSeg) shl 4;
  237.     HiL := LongInt(HighSeg) shl 4;
  238.     Col := StartCol;
  239.     for V := 0 to 255 do begin
  240.       Vec := Vectors[V];
  241.       VeL := (LongInt(OS(Vec).S) shl 4)+OS(Vec).O;
  242.       if (VeL >= LoL) and (VeL < HiL) then begin
  243.         if Col+3 > WrapCol then begin
  244.           {wrap to next line}
  245.           Write(^M^J, '':StartCol-1);
  246.           Col := StartCol;
  247.         end;
  248.         Write(HexB(V), ' ');
  249.         inc(Col, 3);
  250.       end;
  251.     end;
  252.   end;
  253.  
  254.   procedure WriteChained(PspSeg : Word; StartCol, WrapCol : Byte);
  255.     {-Write vectors that WATCH found taken over by a block}
  256.   var
  257.     P : ^ChangeBlock;
  258.     I, MaxChg, Col : Word;
  259.     Found : Boolean;
  260.   begin
  261.     {initialize}
  262.     MaxChg := MemW[WatchPsp:NextChange];
  263.     Col := StartCol;
  264.     Found := False;
  265.     I := 0;
  266.  
  267.     while I < MaxChg do begin
  268.       P := Ptr(WatchPsp, ChangeVectors+I);
  269.       with P^ do
  270.         case ID of
  271.           $00 :           {ChangeBlock describes an active vector takeover}
  272.             if Found then begin
  273.               if Col+3 > WrapCol then begin
  274.                 {wrap to next line}
  275.                 Write(^M^J, '':StartCol-1);
  276.                 Col := StartCol;
  277.               end;
  278.               Write(HexB(Lo(VecNum)), ' ');
  279.               inc(Col, 3);
  280.             end;
  281.           $01 :           {ChangeBlock specifies a disabled takeover}
  282.             if Found then begin
  283.               Write('disabled');
  284.               {Don't write this more than once}
  285.               Exit;
  286.             end;
  287.           $FF :           {ChangeBlock starts a new PSP}
  288.             Found := (PspSeg = PspAdd);
  289.         end;
  290.       inc(I, SizeOf(ChangeBlock));
  291.     end;
  292.   end;
  293.  
  294.   procedure WriteVectors(LowSeg, HighSeg : Word; StartCol, WrapCol : Byte);
  295.     {-Write interrupt vectors either hooked or chained}
  296.   begin
  297.     if UseWatch then
  298.       WriteChained(LowSeg, StartCol, WrapCol)
  299.     else
  300.       WriteHooked(LowSeg, HighSeg, StartCol, WrapCol);
  301.   end;
  302.  
  303.   procedure WriteMcb(McbSeg, PspSeg, Paras, Blocks, Files : Word;
  304.                      Name : String; CmdLine : String);
  305.     {-Write information about one Mcb or group of mcbs}
  306.   var
  307.     Col : Byte;
  308.   begin
  309.     Col := 1;
  310.  
  311.     if ShowSegments then begin
  312.       case McbSeg of
  313.         NoShowVecSeg, ShowVecSeg : ;
  314.       else
  315.         Write(HexW(McbSeg), ' ');
  316.         inc(Col, 5);
  317.       end;
  318.  
  319.       if (PspSeg = 0) or (PspSeg = 8) then
  320.         Write('    ')
  321.       else
  322.         Write(HexW(PspSeg));
  323.       inc(Col, 4);
  324.     end else
  325.       Write('  ');
  326.  
  327.     if ShowBlocks then begin
  328.       Write(' ', Blocks:2);
  329.       inc(Col, 3);
  330.     end;
  331.  
  332.     if ShowFiles then begin
  333.       if Files = 0 then
  334.         Write('   ')
  335.       else
  336.         Write(' ', Files:2);
  337.       inc(Col, 3);
  338.     end;
  339.  
  340.     Write(' ', CommaIze(LongInt(Paras) shl 4, SizeLen),
  341.           ' ', Extend(Name, NameLen),
  342.           ' ', SmartExtend(CmdLine, CmdLen));
  343.     inc(Col, 3+SizeLen+NameLen+CmdLen);
  344.  
  345.     if ShowVectors then
  346.       if (PspSeg = McbSeg+1) or (McbSeg = ShowVecSeg) then
  347.         if ValidPsp(PspSeg) then begin
  348.           Write(' ');
  349.           WriteVectors(PspSeg, PspSeg+Paras, Col+1, 79);
  350.         end;
  351.  
  352.     WriteLn;
  353.  
  354.     {keep track of total reported memory}
  355.     Inc(TotalMem, Paras);
  356.     Inc(TotalMem, Blocks);        {for the mcbs themselves}
  357.   end;
  358.  
  359.   procedure WriteDevices(DevSeg, NextSeg : Word);
  360.     {-Write the DOS 5 device list}
  361.   var
  362.     D : McbPtr;
  363.     Name : String[79];
  364.   begin
  365.     D := Ptr(DevSeg, 0);
  366.     while OS(D).S < NextSeg do begin
  367.       case D^.Id of
  368.         'B' : Name := 'buffers';
  369.         'C' : Name := 'ems buffers';
  370.         'D' : Name := 'device='+Asc2Str(D^.Name);
  371.         'E' : Name := 'device ext';
  372.         'F' : Name := 'files';
  373.         'I' : Name := 'ifs='+Asc2Str(D^.Name);
  374.         'L' : Name := 'lastdrive';
  375.         'S' : Name := 'stacks';
  376.         'X' : Name := 'fcbs';
  377.       else
  378.         Name := '';
  379.       end;
  380.       if Name <> '' then
  381.         WriteLn('':20, CommaIze(LongInt(D^.Len+1) shl 4, 6), ' ', Name);
  382.       D := Ptr(OS(D).S+D^.Len+1, 0);
  383.     end;
  384.   end;
  385.  
  386.   procedure WriteTotalMem;
  387.     {-Write total reported memory with leading space PreSpace}
  388.   var
  389.     PreSpace : Word;
  390.   begin
  391.     if TotalMem <> 0 then begin
  392.       PreSpace := 7;
  393.       if Verbose then
  394.         inc(PreSpace, VerboseIndent);
  395.       WriteLn('':PreSpace, CommaIze(LongInt(TotalMem) shl 4, 8), ' ', TotalName);
  396.       TotalMem := 0;
  397.     end;
  398.   end;
  399.  
  400.   procedure FindTSR;
  401.     {-Find TSRName, report if appropriate, and halt}
  402.  
  403.     procedure FindOne(Start : McbPtr);
  404.     var
  405.       M : McbPtr;
  406.       PspSeg : Word;
  407.       Done : Boolean;
  408.       IsCmd : Boolean;
  409.       Name : String[79];
  410.     begin
  411.       M := Start;
  412.       repeat
  413.         PspSeg := M^.Psp;
  414.         if OS(M).S+1 = PspSeg then begin
  415.           IsCmd := (PspSeg = MemW[PspSeg:$16]);
  416.           if (not IsCmd) and (DosV > 2) and HasEnvironment(HiMemSeg, M) then
  417.             Name := NameFromEnv(M)
  418.           else if DosV >= 4 then
  419.             Name := NameFromMcb(M)
  420.           else if (not IsCmd) and (DosVT >= $031E) then
  421.             Name := NameFromMcb(M)
  422.           else
  423.             Name := '';
  424.           if StUpcase(Name) = TsrName then begin
  425.             if not Quiet then
  426.               WriteLn('Found ', TsrName, ' at ', HexW(PspSeg));
  427.             Halt(0);
  428.           end;
  429.         end;
  430.         Done := (M^.Id = 'Z');
  431.         M := Ptr(OS(M).S+M^.Len+1, 0);
  432.       until Done;
  433.     end;
  434.  
  435.   begin
  436.     if UseLoMem then
  437.       FindOne(Mcb1);
  438.     if UseHiMem then
  439.       FindOne(Ptr(HiMemSeg, 0));
  440.     {Not found if we get here}
  441.     if not Quiet then
  442.       WriteLn('Did not find ', TsrName);
  443.     Halt(2);
  444.   end;
  445.  
  446.   procedure ShowChain(M : McbPtr);
  447.     {-Show chain of blocks starting at M}
  448.   var
  449.     Done : Boolean;
  450.   begin
  451.     repeat
  452.       WriteMcb(OS(M).S, M^.Psp, M^.Len, 1,
  453.                GetFiles(M), GetName(M, ShowDevices), GetCmdLine(M));
  454.       if ShowDevices then
  455.         WriteDevices(OS(M).S+1, OS(M).S+M^.Len+1);
  456.       Done := (M^.Id = 'Z');
  457.       M := Ptr(OS(M).S+M^.Len+1, 0);
  458.     until Done;
  459.     WriteTotalMem;
  460.   end;
  461.  
  462.   procedure WriteVerbose;
  463.     {-Report on each Mcb individually}
  464.   var
  465.     M : McbPtr;
  466.   begin
  467.     Write('Mcb  Psp  Hdl   Size Name           Command Line        ');
  468.     if UseWatch then
  469.       Write('Chained')
  470.     else
  471.       Write('Hooked');
  472.     WriteLn(' Vectors');
  473.     WriteLn('---- ---- --- ------ -------------- ------------------- -----------------------');
  474.  
  475.     if UseLoMem then begin
  476.       {fake Mcb's used by dos itself}
  477.       WriteMcb($0000, $0000, $0040, 0, 0, 'vectors', '');
  478.       WriteMcb($0040, $0000, $0010, 0, 0, 'BIOS data', '');
  479.       WriteMcb($0050, $0000, $0020, 0, 0, 'DOS data', '');
  480.       WriteMcb($0070, $0000, OS(DosList).S-$70, 0, 0, 'sys data', '');
  481.       WriteMcb(OS(DosList).S, $0000, OS(Mcb1).S-OS(DosList).S, 0, 0, 'sys code', '');
  482.       M := Mcb1;
  483.       ShowChain(Mcb1);
  484.     end;
  485.  
  486.     if UseHiMem then begin
  487.       if UseLoMem then
  488.         WriteLn(^M^J'High Memory');
  489.       ShowChain(Ptr(HiMemSeg, 0));
  490.     end;
  491.   end;
  492.  
  493.   procedure SummarizePsp(TPsp, LoMcb, HiMcb : Word);
  494.     {-Write info about all Mcbs in range LoMcb..HiMcb with the specified Psp}
  495.   var
  496.     TM : McbPtr;
  497.     M : McbPtr;
  498.     Size : Word;
  499.     Blocks : Word;
  500.     FakeSeg : Word;
  501.     MPsp : Word;
  502.     Done : Boolean;
  503.     HaveCodeBlock : Boolean;
  504.   begin
  505.     Size := 0;
  506.     Blocks := 0;
  507.     M := Ptr(LoMcb, 0);
  508.     TM := nil;
  509.     HaveCodeBlock := False;
  510.     repeat
  511.       MPsp := M^.Psp;
  512.       if MPsp = 0 then
  513.         MPsp := OS(M).S;
  514.       if MPsp = TPsp then begin
  515.         if (OS(M).S >= LoMcb) and (OS(M).S < HiMcb) then begin
  516.           Inc(Size, M^.Len);
  517.           Inc(Blocks);
  518.           if OS(M).S+1 = TPsp then
  519.             HaveCodeBlock := True;
  520.         end;
  521.         if TM = nil then
  522.           TM := M
  523.         else if M^.Psp = OS(M).S+1 then
  524.           TM := M;
  525.       end;
  526.       Done := (M^.Id = 'Z');
  527.       M := Ptr(OS(M).S+M^.Len+1, 0);
  528.     until Done;
  529.  
  530.     if Blocks > 0 then begin
  531.       if HaveCodeBlock then
  532.         FakeSeg := ShowVecSeg
  533.       else
  534.         FakeSeg := NoShowVecSeg;
  535.       WriteMcb(FakeSeg, TM^.Psp, Size, Blocks, 0,
  536.                GetName(TM, ShowDevices), GetCmdLine(TM));
  537.     end;
  538.   end;
  539.  
  540.   procedure SummarizeRange(LoMcb, HiMcb : Word);
  541.     {-Summarize Psps in the range LoMcb..HiMcb,
  542.       for Psp > 8, Psp < $FFF0, and Psp <> PrefixSeg}
  543.   var
  544.     M : McbPtr;
  545.     MinPsp : Word;
  546.     TPsp : Word;
  547.     PrvPsp : Word;
  548.     Done : Boolean;
  549.   begin
  550.     PrvPsp := 8;
  551.     repeat
  552.       {find the smallest Psp not yet summarized}
  553.       MinPsp := $FFFF;
  554.       M := Ptr(LoMcb, 0);
  555.       repeat
  556.         TPsp := M^.Psp;
  557.         if TPsp = 0 then
  558.           TPsp := OS(M).S;
  559.         if TPsp < MinPsp then
  560.           if (TPsp > PrvPsp) and (TPsp < $FFF0) and (TPsp <> PrefixSeg) then
  561.             MinPsp := TPsp;
  562.         Done := (M^.Id = 'Z');
  563.         M := Ptr(OS(M).S+M^.Len+1, 0);
  564.       until Done;
  565.  
  566.       if MinPsp <> $FFFF then begin
  567.         {add up info about this Psp}
  568.         SummarizePsp(MinPsp, LoMcb, HiMcb);
  569.         {"mark out" this Psp}
  570.         PrvPsp := MinPsp;
  571.       end;
  572.     until MinPsp = $FFFF;
  573.   end;
  574.  
  575.   procedure SummarizeDos(LoMcb, HiMcb : Word);
  576.     {-Sum up memory attributed to DOS}
  577.   var
  578.     M : McbPtr;
  579.     Size : Word;
  580.     Blocks : Word;
  581.     FakeSeg : Word;
  582.     Done : Boolean;
  583.   begin
  584.     M := Ptr(LoMcb, 0);
  585.     Size := 0;
  586.     Blocks := 0;
  587.     repeat
  588.       if M^.Psp = 8 then
  589.         if (OS(M).S >= LoMcb) and (OS(M).S < HiMcb) then begin
  590.           Inc(Size, M^.Len);
  591.           Inc(Blocks);
  592.         end;
  593.       Done := (M^.Id = 'Z');
  594.       M := Ptr(OS(M).S+M^.Len+1, 0);
  595.     until Done;
  596.     if Blocks > 0 then begin
  597.       if HiMcb > TopSeg then
  598.         FakeSeg := NoShowVecSeg
  599.       else
  600.         FakeSeg := ShowVecSeg;
  601.       WriteMcb(FakeSeg, $00, OS(Mcb1).S+Size, Blocks, 0, 'DOS', '');
  602.     end;
  603.   end;
  604.  
  605.   procedure SummarizeFree(LoMcb, HiMcb : Word);
  606.     {-Write the free memory blocks in specified range of Mcbs}
  607.   var
  608.     M : McbPtr;
  609.     Done : Boolean;
  610.   begin
  611.     M := Ptr(LoMcb, 0); {!!}
  612.     {M := Mcb1;}        {!!}
  613.     repeat
  614.       if (M^.Psp = 0) and (M^.Len > 0) and
  615.          (OS(M).S >= LoMcb) and (OS(M).S < HiMcb) then
  616.         WriteMcb(NoShowVecSeg, $0000, M^.Len, 1, 0, FreeName, '');
  617.       Done := (M^.Id = 'Z');
  618.       M := Ptr(OS(M).S+M^.Len+1, 0);
  619.     until Done;
  620.   end;
  621.  
  622.   procedure WriteCondensed;
  623.     {-Report on Mcb's by Psp}
  624.   begin
  625.     Write('Psp  Cnt   Size Name       Command Line        ');
  626.     if UseWatch then
  627.       Write('Chained')
  628.     else
  629.       Write('Hooked');
  630.     WriteLn(' Vectors');
  631.     WriteLn('---- --- ------ ---------- ------------------- --------------------------------');
  632.  
  633.     if UseLoMem then begin
  634.       SummarizeDos(OS(Mcb1).S, TopSeg-1);  {DOS memory usage}
  635.       SummarizeRange(OS(Mcb1).S, TopSeg-1);{programs loaded in low memory}
  636.       SummarizePsp(PrefixSeg, OS(Mcb1).S, $FFFF);   {current program free space}
  637.       WriteTotalMem;                       {sum of memory so far}
  638.     end;
  639.  
  640.     if UseHiMem then begin
  641.       if UseLoMem then
  642.         WriteLn(^M^J'High Memory');
  643.       SummarizeDos(HiMemSeg, $FFFF);
  644.       SummarizeRange(HiMemSeg, $FFFF);
  645.       WriteTotalMem;
  646.     end;
  647.   end;
  648.  
  649.   procedure WriteFree;
  650.     {-Show just the free blocks in conventional memory}
  651.   begin
  652.     if UseLoMem then begin
  653.       WriteLn('Normal Memory');
  654.       SummarizeFree(OS(Mcb1).S, TopSeg-1);         {free blocks in low memory}
  655.       SummarizePsp(PrefixSeg, OS(Mcb1).S, $FFFF);  {current program free space}
  656.     end;
  657.     if UseHiMem then begin
  658.       if UseLoMem then
  659.         WriteLn(^M^J'High Memory');
  660.       SummarizeFree(HiMemSeg, $FFFF);
  661.     end;
  662.   end;
  663.  
  664.   procedure WriteSummary;
  665.     {-Write "summary" report for conventional memory}
  666.   begin
  667.     WriteLn('      Size Name       Command Line');
  668.     WriteLn('---------- ---------- --------------------------------------------------------');
  669.  
  670.     if UseLoMem then begin
  671.       SummarizeDos(OS(Mcb1).S, TopSeg-1);            {DOS memory usage}
  672.       SummarizeRange(OS(Mcb1).S, TopSeg-1);          {programs loaded in low memory}
  673.       SummarizePsp(PrefixSeg, OS(Mcb1).S, $FFFF);    {current program free space}
  674.     end;
  675.     if UseHiMem then begin
  676.       if UseLoMem then
  677.         WriteLn(^M^J'High Memory');
  678.       SummarizeDos(HiMemSeg, $FFFF);
  679.       SummarizeRange(HiMemSeg, $FFFF);
  680.     end;
  681.   end;
  682.  
  683.   procedure ShowConventionalMem;
  684.     {-Report on conventional memory, low and high}
  685.   begin
  686.     {Default values for display}
  687.     ShowSegments := True;
  688.     ShowBlocks := False;
  689.     ShowFiles := False;
  690.     ShowVectors := True;
  691.     SizeLen := 7;
  692.     NameLen := 10;
  693.     CmdLen := 19;
  694.  
  695.     if ShowFree then begin
  696.       ShowSegments := False;
  697.       ShowVectors := False;
  698.       WriteFree;
  699.     end else if ShowSummary then begin
  700.       ShowSegments := False;
  701.       ShowVectors := False;
  702.       CmdLen := 56;
  703.       WriteSummary;
  704.     end else if Verbose then begin
  705.       ShowFiles := True;
  706.       NameLen := 14;
  707.       WriteVerbose;
  708.     end else begin
  709.       ShowBlocks := True;
  710.       WriteCondensed;
  711.     end;
  712.   end;
  713.  
  714.   procedure ShowTheEmsMem;
  715.   var
  716.     Handles : Word;
  717.     H : Word;
  718.     P : Word;
  719.     Pages : LongInt;
  720.     EmsV : Byte;
  721.     PreSpace : Byte;
  722.     Name : string[9];
  723.     PageMap : PageArray;
  724.   begin
  725.     if not EmsPresent then
  726.       Exit;
  727.     WriteLn;
  728.     WriteLn('EMS Memory');
  729.     if not(ShowFree or ShowSummary) then begin
  730.       EmsV := EmsVersion;
  731.       Handles := EmsHandles(PageMap);
  732.       if Handles > 0 then
  733.         for H := 1 to Handles do begin {!!}
  734.           P := PageMap[H].NumPages;
  735.           if P <> 0 then begin
  736.             Write(HexW(H), ' ');
  737.             if Verbose then
  738.               Write('':VerboseIndent);
  739.             Write(CommaIze(LongInt(P) shl 14, 10));
  740.             if EmsV >= $40 then begin
  741.               GetHandleName(PageMap[H].Handle, Name);
  742.               if Name = '' then
  743.                 Name := 'n/a';
  744.             end else
  745.               Name := 'n/a';
  746.             WriteLn(' ', Name);
  747.           end;
  748.         end;
  749.     end;
  750.     Pages := EmsPagesAvailable;
  751.     if ShowFree or ShowSummary then
  752.       PreSpace := 0
  753.     else
  754.       PreSpace := 5;
  755.     if Verbose then
  756.       inc(PreSpace, VerboseIndent);
  757.     WriteLn('':PreSpace, CommaIze(LongInt(OS(Pages).O) shl 14, 10), ' ', FreeName);
  758.     if ShowSummary or (not ShowFree) then
  759.       WriteLn('':PreSpace, CommaIze(LongInt(OS(Pages).S) shl 14, 10), ' ', TotalName);
  760.   end;
  761.  
  762.   procedure ShowTheXmsMem;
  763.     {-Show what we can about XMS}
  764.   label
  765.     ExitPoint;
  766.   var
  767.     FMem : Word;
  768.     FMax : Word;
  769.     XHandles : Word;
  770.     H : Word;
  771.     HMem : Word;
  772.     Total : Word;
  773.     XmsPages : XmsHandlesPtr;
  774.     Status : Byte;
  775.     PreSpace : Byte;
  776.   begin
  777.     if not XmsInstalled then
  778.       Exit;
  779.     Status := QueryFreeExtMem(FMem, FMax);
  780.     if Status = $A0 then begin
  781.       FMem := 0;
  782.       FMax := 0;
  783.     end else if Status <> 0 then
  784.       Exit;
  785.  
  786.     {Total will count total XMS memory}
  787.     Total := 0;
  788.  
  789.     WriteLn(^M^J'XMS Memory');
  790.     GotXms := not Verbose;
  791.  
  792.     if ShowFree then
  793.       goto ExitPoint;
  794.  
  795.     {Get an array containing handles}
  796.     XHandles := GetXmsHandles(XmsPages);
  797.  
  798.     {Report all the handles}
  799.     for H := 1 to XHandles do begin
  800.       HMem := XmsPages^[H].NumPages;
  801.       if not ShowSummary then begin
  802.         Write(HexW(H), ' ');
  803.         if Verbose then
  804.           Write('':VerboseIndent);
  805.         WriteLn(CommaIze(LongInt(HMem) shl 10, 10), ' n/a');
  806.       end;
  807.       inc(Total, HMem);
  808.     end;
  809.  
  810.     {Add the free memory to the total}
  811.     inc(Total, FMem);
  812.  
  813. ExitPoint:
  814.     if ShowFree or ShowSummary then
  815.       PreSpace := 0
  816.     else
  817.       PreSpace := 5;
  818.     if Verbose then
  819.       inc(PreSpace, VerboseIndent);
  820.     WriteLn('':PreSpace, CommaIze(LongInt(FMem) shl 10, 10), ' ', FreeName);
  821.     if Total <> 0 then
  822.       WriteLn('':PreSpace, CommaIze(LongInt(Total) shl 10, 10), ' ', TotalName);
  823.   end;
  824.  
  825.   procedure ShowTheExtendedMem;
  826.   var
  827.     Total : LongInt;
  828.     PreSpace : Byte;
  829.   begin
  830.     if GotXms or ShowFree then
  831.       Exit;
  832.     if ExtMemPossible then
  833.       Total := ExtMemTotalPrim
  834.     else
  835.       Total := 0;
  836.     if Total = 0 then
  837.       Exit;
  838.  
  839.     WriteLn(^M^J'Raw Extended Memory');
  840.     if ShowSummary then
  841.       PreSpace := 0
  842.     else
  843.       PreSpace := 5;
  844.     if Verbose then
  845.       inc(PreSpace, VerboseIndent);
  846.     WriteLn('':PreSpace, CommaIze(Total, 10), ' ', TotalName);
  847.   end;
  848.  
  849.   procedure WriteCopyright;
  850.     {-Write a copyright message}
  851.   begin
  852.     Write('MAPMEM ', Version, ', Copyright 1993 TurboPower Software'^M^J);
  853.   end;
  854.  
  855.   procedure Initialize;
  856.     {-Initialize various global variables}
  857.   begin
  858.     GotXms := False;
  859.     TotalMem := 0;
  860.     TopSeg := TopOfMemSeg;
  861.   end;
  862.  
  863.   procedure GetOptions;
  864.     {-Parse command line and set options}
  865.   var
  866.     Arg : String[127];
  867.  
  868.     procedure WriteHelp;
  869.     begin
  870.       WriteCopyright;
  871.       WriteLn;
  872.       WriteLn('MAPMEM produces a report showing what memory resident programs are installed,');
  873.       WriteLn('how much memory each uses, and what interrupt vectors are taken over.');
  874.       WriteLn;
  875.       WriteLn('MAPMEM accepts the following command line syntax:');
  876.       WriteLn;
  877.       WriteLn('  MAPMEM [Options]');
  878.       WriteLn;
  879.       WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  880.       WriteLn;
  881.       WriteLn('     /C name  check whether TSR "name" is loaded.');
  882.       WriteLn('     /E       report expanded (EMS) memory.');
  883.       WriteLn('     /F       report free areas only.');
  884.       WriteLn('     /H       do not use WATCH information for vectors.');
  885.       WriteLn('     /L       do not report low memory blocks (<640K).');
  886.       WriteLn('     /Q       write no screen output with /C option.');
  887.       WriteLn('     /S       show summary of all memory areas.');
  888.       WriteLn('     /U       report upper memory blocks if available.');
  889.       WriteLn('     /V       verbose report.');
  890.       WriteLn('     /X       report extended (XMS) memory.');
  891.       WriteLn('     /?       write this help screen.');
  892.       Halt(1);
  893.     end;
  894.  
  895.     procedure UnknownOption;
  896.     begin
  897.       WriteCopyright;
  898.       WriteLn('Unknown command line option: ', Arg);
  899.       Halt(1);
  900.     end;
  901.  
  902.     procedure BadOption;
  903.     begin
  904.       WriteCopyright;
  905.       WriteLn('Invalid command line option: ', Arg);
  906.       Halt(1);
  907.     end;
  908.  
  909.     procedure GetArgs(S : String);
  910.     var
  911.       SPos : Word;
  912.     begin
  913.       SPos := 1;
  914.       repeat
  915.         Arg := NextArg(S, SPos);
  916.         if Arg = '' then
  917.           Exit;
  918.         if Arg = '?' then
  919.           WriteHelp
  920.         else
  921.           case Arg[1] of
  922.             '-', '/' :
  923.               case Length(Arg) of
  924.                 1 : BadOption;
  925.                 2 : case Upcase(Arg[2]) of
  926.                       '?' : WriteHelp;
  927.                       'C' : begin
  928.                               CheckTSR := not CheckTSR;
  929.                               if CheckTSR then begin
  930.                                 TSRName := StUpcase(NextArg(S, SPos));
  931.                                 if TSRName = '' then begin
  932.                                   WriteCopyright;
  933.                                   WriteLn('TSR name to check for is missing');
  934.                                   Halt(1);
  935.                                 end;
  936.                               end;
  937.                             end;
  938.                       'E' : ShowEmsMem := not ShowEmsMem;
  939.                       'F' : ShowFree := not ShowFree;
  940.                       'H' : UseWatch := not UseWatch;
  941.                       'L' : UseLoMem := not UseLoMem;
  942.                       'Q' : Quiet := not Quiet;
  943.                       'S' : ShowSummary := not ShowSummary;
  944.                       'U' : UseHiMem := not UseHiMem;
  945.                       'V' : Verbose := not Verbose;
  946.                       'X' : ShowExtMem := not ShowExtMem;
  947.                     else
  948.                       BadOption;
  949.                     end;
  950.               else
  951.                 UnknownOption;
  952.               end;
  953.           else
  954.             UnknownOption;
  955.           end;
  956.       until False;
  957.     end;
  958.  
  959.   begin
  960.     TsrName := '';
  961.  
  962.     {Get arguments from the command line and the environment}
  963.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  964.     GetArgs(GetEnv('MAPMEM'));
  965.  
  966.     {Account for related options}
  967.     if ShowFree then
  968.       ShowSummary := False;
  969.     if not UseLoMem then
  970.       UseHiMem := True;
  971.     if ShowFree or ShowSummary then begin
  972.       UseLoMem := True;
  973.       UseHiMem := True;
  974.       ShowEmsMem := True;
  975.       ShowExtMem := True;
  976.       Verbose := False;
  977.     end;
  978.     if not CheckTSR then
  979.       Quiet := False;
  980.  
  981.     {Initialize for high memory access}
  982.     HiMemSeg := FindHiMemStart;
  983.     if HiMemSeg = 0 then
  984.       UseHiMem := False;
  985.  
  986.     {Don't report any vectors normally taken over by SYSTEM}
  987.     SwapVectors;
  988.  
  989.     {ExitProc will undo swap and restore high memory access}
  990.     SaveExit := ExitProc;
  991.     ExitProc := @SafeExit;
  992.  
  993.     {Find WATCH in memory if requested}
  994.     if UseWatch then begin
  995.       WatchPsp := WatchPspSeg;
  996.       if WatchPsp = 0 then
  997.         UseWatch := False;
  998.     end;
  999.  
  1000.     if not Quiet then
  1001.       WriteCopyright;
  1002.   end;
  1003.  
  1004. begin
  1005.   {$IFDEF MeasureStack}
  1006.   FillChar(Mem[SSeg:0], SPtr-16, $AA);
  1007.   {$ENDIF}
  1008.  
  1009.   Initialize;
  1010.   GetOptions;
  1011.   if CheckTSR then
  1012.     FindTSR
  1013.   else begin
  1014.     WriteLn;
  1015.     ShowConventionalMem;
  1016.     if ShowEmsMem then
  1017.       ShowTheEmsMem;
  1018.     if ShowExtMem then begin
  1019.       ShowTheXmsMem;
  1020.       ShowTheExtendedMem;
  1021.     end;
  1022.   end;
  1023.  
  1024.   {$IFDEF MeasureStack}
  1025.   I := 0;
  1026.   while I < SPtr-16 do
  1027.     if Mem[SSeg:i] <> $AA then begin
  1028.       writeln('Unused stack ', i, ' bytes');
  1029.       I := SPtr;
  1030.     end else
  1031.       inc(I);
  1032.   {$ENDIF}
  1033. end.
  1034.